﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using DiscoveryLogic.Common.Utilities;
using DiscoveryLogic.Common.Utilities.TextMatch;

namespace DiscoveryLogic.Analytics.DataObjects.Networks
{
    public class NetworkCluster:EntityBase 
    {
        private int _ClusterID;
        /// <summary></summary>
        public int ClusterID
        {
            get
            {
                return this._ClusterID;
            }
            set
            {
                this._ClusterID = value;
            }
        }

        private int _ParentClusterID;
        /// <summary></summary>
        public int ParentClusterID
        {
            get
            {
                return this._ParentClusterID;
            }
            set
            {
                this._ParentClusterID = value;
            }
        }

        private string _ClusterName;
        /// <summary></summary>
        public string ClusterName
        {
            get
            {
                return this._ClusterName;
            }
            set
            {
                this._ClusterName = value;
            }
        }

        private List<EntityBase> _ChildEntities;
        /// <summary></summary>
        public List<EntityBase> ChildEntities
        {
            get
            {
                return this._ChildEntities;
            }
            set
            {
                this._ChildEntities = value;
            }
        }

        private List<NetworkCluster> _ChildClusters;
        /// <summary></summary>
        public List<NetworkCluster> ChildClusters
        {
            get
            {
                return this._ChildClusters;
            }
            set
            {
                this._ChildClusters = value;
            }
        }

        public NetworkCluster(
            int clusterID, int parentClusterID,
            string clusterName)
            : base(EntityType.Cluster, "ClusterID", "ClusterName")
        {
            this._ClusterID = clusterID;
            this._ParentClusterID = parentClusterID;
            this._ClusterName = clusterName;
            this._ChildEntities = new List<EntityBase>();
            this._ChildClusters = new List<NetworkCluster>();
        }

        #region xml

        public override void Save(ref XmlDocument xDoc, XmlNode clusterNode)
        {
            XmlDataUtil.UpdateAttribute(ref xDoc, clusterNode, "ClusterID", this.ClusterID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, clusterNode, "ParentClusterID", this.ParentClusterID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, clusterNode, "ClusterName",this.ClusterName);
            if(this.ChildEntities !=null && this.ChildEntities.Count>0)
            {
                XmlNode entityNodes = XmlDataUtil.AddElement(ref xDoc, clusterNode, "Entities");
                foreach(EntityBase entity in this.ChildEntities)
                {
                    XmlNode entityNode = XmlDataUtil.AddElement(ref xDoc, entityNodes, "Entity");
                    XmlDataUtil.UpdateAttribute(ref xDoc, entityNode, "EntityType", entity.EntityType.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, entityNode, "Key", entity.PKValue());
                }
            }
            if(this.ChildClusters !=null && this.ChildClusters.Count>0)
            {
                XmlNode childClusterNodes = XmlDataUtil.AddElement(ref xDoc, clusterNode, "ChildClusters");
                foreach(NetworkCluster childCluster in this.ChildClusters)
                {
                    XmlNode childClusterNode = XmlDataUtil.AddElement(ref xDoc, childClusterNodes, "ChildCluster");
                    XmlDataUtil.UpdateAttribute(ref xDoc, childClusterNode, "ClusterID", childCluster.ClusterID.ToString());
                }
            }
            
        }

        public static void Save(string networkName, List<NetworkCluster> clusters)
        {
            string filePath = Path.Combine(
                DataFolder.NetworkDataFolder, networkName + DataFolder.NetworkClusterFileExtension);
            if(File.Exists(filePath))
                File.Delete(filePath);
            XmlDocument xDoc = new XmlDocument();
            xDoc.LoadXml("<NetworkClusters></NetworkClusters>");
            XmlNode root = xDoc.DocumentElement;
            XmlDataUtil.UpdateAttribute(ref xDoc, root, "NetworkName", networkName);

            foreach(NetworkCluster cluster in clusters)
            {
                XmlNode clusterNode = XmlDataUtil.AddElement(ref xDoc, root, "Cluster");
                cluster.Save(ref xDoc, clusterNode);
            }

            xDoc.Save(filePath);
        }

        public static Dictionary<int, NetworkCluster> Read(string networkName)
        {
            string filePath = Path.Combine(
                DataFolder.NetworkDataFolder, networkName + DataFolder.NetworkClusterFileExtension);
            if(!File.Exists(filePath))
                return new Dictionary<int, NetworkCluster>();

            Dictionary<int, NetworkCluster> clusters = new Dictionary<int, NetworkCluster>();
            Network network = Network.Read(networkName);
            Dictionary<EntityType, Dictionary<string, EntityBase>> entities = network.GetEntities();

            XmlDocument xDoc=new XmlDocument();
            xDoc.Load(filePath);
            XmlNode root = xDoc.DocumentElement;
            XmlNodeList clusterNodes = root.SelectNodes("Cluster");
            Dictionary<int,List<int>> clusterChildClusterIDs=new Dictionary<int, List<int>>();

            if(clusterNodes !=null && clusterNodes.Count>0)
            {
                foreach(XmlNode clusterNode in clusterNodes)
                {
                    int clusterID = int.Parse(XmlDataUtil.GetAttributeValue(clusterNode, "ClusterID", "0"));
                    int parentClusterID = int.Parse(XmlDataUtil.GetAttributeValue(clusterNode, "ParentClusterID", "0"));
                    string clusterName = XmlDataUtil.GetAttributeValue(clusterNode, "ClusterName", "");
                    NetworkCluster cluster=new NetworkCluster(clusterID, parentClusterID, clusterName);

                    XmlNodeList childEntityNodes = clusterNode.SelectNodes("Entities/Entity");
                    if(childEntityNodes !=null && childEntityNodes.Count>0)
                    {
                        cluster.ChildEntities=new List<EntityBase>();
                        foreach(XmlNode entityNode in childEntityNodes)
                        {
                            EntityType entityType =
                                (EntityType)
                                Enum.Parse(
                                    typeof (EntityType),
                                    XmlDataUtil.GetAttributeValue(
                                        entityNode, "EntityType", EntityType.Grants.ToString()));
                            string key = XmlDataUtil.GetAttributeValue(entityNode, "Key", "");
                            EntityBase childEntity = entities[entityType][key];
                            cluster.ChildEntities.Add(childEntity);
                        }
                    }

                    XmlNodeList childClusterNodes = clusterNode.SelectNodes("ChildClusters/ChildCluster");
                    if(childClusterNodes !=null && childClusterNodes.Count>0)
                    {
                        List<int> childClusterIDs=new List<int>();
                        cluster.ChildClusters=new List<NetworkCluster>();
                        foreach(XmlNode childClusterNode in childClusterNodes)
                        {
                            int childClusterID =
                                int.Parse(XmlDataUtil.GetAttributeValue(childClusterNode, "ClusterID", "0"));
                            childClusterIDs.Add(childClusterID);
                        }
                        clusterChildClusterIDs.Add(clusterID, childClusterIDs);
                    }

                    
                    clusters.Add(clusterID, cluster);
                }

                List<int> clusterIDs = clusters.Keys.ToList();
                foreach (int clusterID in clusterIDs)
                {
                    if (clusterChildClusterIDs.ContainsKey(clusterID))
                    {
                        List<NetworkCluster> childClusters=new List<NetworkCluster>();
                        foreach(int childClusterID in clusterChildClusterIDs[clusterID])
                        {
                            NetworkCluster childCluster = clusters[childClusterID];
                            childClusters.Add(childCluster);
                        }
                        clusters[clusterID].ChildClusters = childClusters;
                    }
                }
            }
            return clusters;
        }
        #endregion

        #region method
        public void GetAllNestedEntityKeys(ref Dictionary<EntityType, List<string>> entities)
        {
            if (this.ChildEntities != null && this.ChildEntities.Count > 0)
            {
                foreach (EntityBase fromEntity in this.ChildEntities)
                {
                    if (entities.ContainsKey(fromEntity.EntityType))
                    {
                        List<string> keys = entities[fromEntity.EntityType];
                        if (!keys.Contains(fromEntity.PKValue()))
                            keys.Add(fromEntity.PKValue());
                        entities[fromEntity.EntityType] = keys;
                    }
                    else
                    {
                        List<string> keys=new List<string>();
                        keys.Add(fromEntity.PKValue());
                        entities.Add(fromEntity.EntityType, keys);
                    }
                }
            }

            if(this.ChildClusters !=null && this.ChildClusters.Count>0)
            {
                foreach(NetworkCluster childCluster in this.ChildClusters)
                {
                    childCluster.GetAllNestedEntityKeys(ref entities);
                }
            }
        }
        #endregion

        
    }
}
